#include <device.h>
#include <loader.h>
#include <errors.h>
#include <syscalls.h> 
#include <string.h>
#include <i386.h>
#include <debug.h>

//tabla de asociaciones de fd
device * pid_fd[MAX_PID+1][MAX_FD+1];

int device_descriptor(chardev* dev) {

	int i;

	//busco un fd libre
	for (i = 0; i < MAX_FD; i++) {
		if(NULL == pid_fd[current_pid][i]) {
			pid_fd[current_pid][i] = (device *) dev;
			return i;
		}
	}
	dev->refcount++;

	return -ENOFD;
}

int device_free_descriptor(int fd) {
	
	if (fd < 0 || fd > MAX_FD) {
		return -EINVFD;
	}
	
	pid_fd[current_pid][fd] = NULL;
	return fd;
}

void device_init(void) {

	//limpio la tabla de fd
	memset(pid_fd, 0, MAX_PID * MAX_FD * sizeof(device *));
	
}

void
device_debug()
{
	debug_log(" PID %x \n", current_pid);
	int i;
	
	for (i = 0; i < MAX_FD; i++) {
		if(NULL != pid_fd[current_pid][i])
		{
			debug_log("FD %d ADDR %x REF %d\n", i, pid_fd[current_pid][i], pid_fd[current_pid][i]->refcount);
		}
	}
	debug_log(" END \n");
	breakpoint();
}
int read(int fd, void* buf, uint_32 size) {

	device * dev;

	if (fd < 0 || fd > MAX_FD) {
		return -EINVFD;
	}

	dev = pid_fd[current_pid][fd];
	if (NULL == dev) {
		return -ENOFD;
	}

	if (CHARDEV != dev->clase) {
		return -EINVDEV;
	}

	chardev *cdev = (chardev*) dev;
	if (NULL == cdev->read) {
		return -ENOFUNC;
	}
	return cdev->read(cdev, buf, size);
}

int write(int fd, const void* buf, uint_32 size) {

	device * dev;

	if (fd < 0 || fd > MAX_FD) {
		return -EINVFD;
	}

	dev = pid_fd[current_pid][fd];
	if (NULL == dev) {
		debug_log(" FD %d \n", fd);
		device_debug();
		return -ENOFD;
	}
 
	if (CHARDEV != dev->clase) {
		return -EINVDEV;
	}

	chardev *cdev = (chardev*) dev;
	if (NULL == cdev->write) {
		return -ENOFUNC;
	}

	return cdev->write(cdev, buf, size);
}

int seek(int fd, uint_32 pos) {

	device * dev;

	if (fd < 0 || fd > MAX_FD) {
		return -EINVFD;
	}

	dev = pid_fd[current_pid][fd];
	if (NULL == dev) {
		return -ENOFD;
	}

	if (CHARDEV != dev->clase) {
		return -EINVDEV;
	}

	chardev *cdev = (chardev*) dev;
	if (NULL == cdev->seek) {
		return -ENOFUNC;
	}
	return cdev->seek(cdev, pos);
}

int flush(int fd) {

	device * dev;

	if (fd < 0 || fd > MAX_FD) {
		return -EINVFD;
	}

	dev = pid_fd[current_pid][fd];
	if (NULL == dev) {
		return -ENOFD;
	}

	if (CHARDEV != dev->clase) {
		return -EINVDEV;
	}

	chardev *cdev = (chardev*) dev;
	if (NULL == cdev->flush) {
		return -ENOFUNC;
	}
	
	if (cdev->refcount > 1) {
		cdev->refcount--;
		return 0;
	}

	pid_fd[current_pid][fd] = NULL;
	return cdev->flush(cdev);
}

void
device_fork(pid parent, pid child) {

	int i;

	//busco un fd libre
	for (i = 0; i < MAX_FD; i++) {
		if(NULL != pid_fd[parent][i])
		{
			pid_fd[child][i] = pid_fd[parent][i];
			pid_fd[child][i]->refcount++;
		}
	}
}

void
device_flush_all(pid pid)
{
	int i;

	//busco un fd libre
	for (i = 0; i < MAX_FD; i++) {
		if(NULL != pid_fd[pid][i])
		{
			device * dev = pid_fd[pid][i];
			if (CHARDEV != dev->clase)
				continue;
				
			chardev *cdev = (chardev*) dev;
			
			
			if (cdev->refcount > 1) {
				cdev->refcount--;
				continue;
			}
			
			if (NULL == cdev->flush) 
				continue;

			pid_fd[pid][i] = NULL;
			
			cdev->flush(cdev);
		}
	}
}

void
device_clean(pid pid)
{
	int i;

	//busco un fd libre
	for (i = 0; i < MAX_FD; i++) {
		pid_fd[pid][i] = NULL;
	}
}

